[TrimmableTypeMap] Fix stale legacy JCW contamination when switching typemap flavors#11098
Draft
simonrozsival wants to merge 3 commits intomainfrom
Draft
[TrimmableTypeMap] Fix stale legacy JCW contamination when switching typemap flavors#11098simonrozsival wants to merge 3 commits intomainfrom
simonrozsival wants to merge 3 commits intomainfrom
Conversation
de004d7 to
36b07f1
Compare
36b07f1 to
e628b09
Compare
…typemap flavors When switching MonoAndroidTypeMapFlavor between legacy and trimmable without a clean build, stale legacy JCWs with Runtime.register(typeName, klass, methods) calls persisted in the android/src/ intermediate directory alongside trimmable JCWs that only use Runtime.registerNatives(). Both got compiled into the APK, causing the legacy registration path to execute at runtime — a path incompatible with trimming. Root cause: the trimmable JCW generator wrote to typemap/java/ and then copied to android/src/, but never cleaned android/src/ first. Legacy JCWs with different package names survived the copy overlay. Three fixes: 1. Write trimmable JCWs directly to _AndroidIntermediateJavaSourceDirectory (android/src/) instead of a separate typemap/java/ directory, eliminating the copy step entirely. This is the same directory that _FindJavaStubFiles globs from, so no files can be missed or stale. 2. Add MonoAndroidTypeMapFlavor to the build properties cache so switching between legacy and trimmable triggers _CleanIntermediateIfNeeded, removing all stale artifacts from the previous flavor. 3. Always wire registerJniNativesFn so that if a stale JCW somehow calls Runtime.register(), TrimmableTypeMapTypeManager.RegisterNativeMembers throws UnreachableException with a clear diagnostic instead of the C++ side silently dropping the call. Also adds a unit test verifying trimmable JCWs never emit Runtime.register(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
e628b09 to
d3cfd10
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When switching
_AndroidTypeMapImplementationbetweenllvm-irandtrimmablewithout a clean build, stale legacy JCWs contaminate the trimmable APK.What happens
llvm-ir:GenerateJavaCallableWrapperswrites JCWs toandroid/src/withRuntime.register(typeName, klass, methods)calls.trimmable:GenerateTrimmableTypeMapemits JCWs that should only callRuntime.registerNatives(klass), then places them inandroid/src/._FindJavaStubFilesglobs all*.javafromandroid/src/, including stale legacy JCWs.classes.dex.Runtime.register(), triggering the reflection-based registration path that is incompatible with trimming.Previously, the C++ side silently dropped
Runtime.register()calls becauseregisterJniNativesFnwasnullptrfor trimmable mode, which masked the bug.Fix (three layers)
1. Write JCWs directly to
_AndroidIntermediateJavaSourceDirectoryThe trimmable generator now writes to
_AndroidIntermediateJavaSourceDirectory(android/src/) directly instead oftypemap/java/+ copy. This removes the extra overlay step where stale files could survive.2. Add
_AndroidTypeMapImplementationto the properties cacheSwitching typemap implementations now triggers
_CleanIntermediateIfNeeded, which clears stale intermediate artifacts includingandroid/src/.3. Always wire
registerJniNativesFnInstead of silently dropping
Runtime.register()calls in C++, the function pointer is always set. If a stale JCW somehow still callsRuntime.register(), the call now flows through to C# whereTrimmableTypeMapTypeManager.RegisterNativeMembersthrowsUnreachableException.Tests
JcwJavaSourceGeneratorTests): verifies trimmable JCWs emitRuntime.registerNatives (klass)and never emit the legacyRuntime.register("path.BuildTest.SwitchingTypeMapImplementationTriggersClean): verifies that switching_AndroidTypeMapImplementationbetweenllvm-irandtrimmabletriggers_CleanIntermediateIfNeeded, while a no-change rebuild skips it.Files changed
JNIEnvInit.cs— remove theif (!TrimmableTypeMap)guard onregisterJniNativesFnTrimmable.targets— write JCWs to_AndroidIntermediateJavaSourceDirectorydirectly and remove the extra output/copy stepCommon.targets— add_AndroidTypeMapImplementationto_PropertyCacheItemsJcwJavaSourceGeneratorTests.cs— regression coverage for positiveregisterNativesand negativeRuntime.registerassertionsBuildTest.cs— integration test for typemap implementation switching